<!DOCTYPE html>
<html lang="en">

<head>
  <meta charset="UTF-8">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <meta http-equiv="X-UA-Compatible" content="ie=edge">
  <title>Document</title>
</head>

<body>
  <canvas id="canvas" width="300" height="300"></canvas>
  <script>
    // 获取canvas元素
    let canvas = document.getElementById('canvas');
    let ctx = canvas.getContext("2d");
    let width = canvas.width;
    let height = canvas.height;

    // defaultAngle 设置可滚动圆环的起始角度，通过requestAnimationFrame函数一点一点的递增来使圆环跑起来,
    // 也可以使用setTimeout()
    // 与圆环的起始角度相同
    let defaultAngle = Math.PI * 0.75;

    animate();
    // 逐帧动画
    function animate()
    {
      defaultAngle += 0.05;
      // 这个值可以根据个人需要修改
      // 这个maxAngle 时上层圆环滚动的到最大的角度可以传入这个角度来控制滚动的百分比
      let maxAngle = Math.PI * 2 + Math.PI * 0.25;
      if (defaultAngle >= maxAngle) {
        defaultAngle = maxAngle;
        draw(); // 绘制圆环
        circlingPointer() // 绘制最上面的指针
        return
      }
      draw(); // 绘制圆环
      circlingPointer() // 绘制最上面的指针
      window.requestAnimationFrame(animate);
    }
    // 绘制圆环
    function draw()
    {
      // 为了避免每次绘制的时候出现一些奇奇怪怪的问题，比如拖影之类的，每次绘制之前清空一次绘布
      ctx.clearRect(0, 0, width, height);
      // 外环
      let circleObj = {
        ctx: ctx,
        /*圆心*/
        x: width / 2,
        y: height / 2,
        /*半径*/
        radius: width / 2 - 20, //半径比canvas宽的一半要小
        /*环的宽度*/
        lineWidth: 20
      };
      // 内环
      let smallCircle = {
        ctx: ctx,
        x: width / 2,
        y: height / 2,
        radius: width / 2 - 60,
        lineWidth: 6,
        color: '#eee',
        startAngle: 0,
        endAngle: Math.PI * 2
      };

      // 绘制内环背景
      drawCircle(smallCircle);
      // 绘制内环 根据defaultAngle的角度变化而滚动
      smallCircle.startAngle = Math.PI * 0.75;
      smallCircle.endAngle = defaultAngle;
      // 使用ctx.createLinearGradient来为圆填充渐变色
      let smallGrd = ctx.createLinearGradient(width / 2, 0, 0, height);
      smallGrd.addColorStop(0, "#dd6200");
      smallGrd.addColorStop(1, "#fff400");
      smallCircle.color = smallGrd;
      drawCircle(smallCircle);
      // 绘制外环背景
      let bgrd = ctx.createLinearGradient(width / 2, 0, 0, height);
      bgrd.addColorStop(0, "#95ea5c");
      bgrd.addColorStop(1, "#f8d6c1");
      circleObj.color = bgrd;
      circleObj.startAngle = Math.PI * 0.75;
      circleObj.endAngle = Math.PI * 2 + Math.PI * 0.25;
      drawCircle(circleObj);
      // 绘制外环
      circleObj.startAngle = Math.PI * 0.75;
      circleObj.endAngle = defaultAngle;

      let grd = ctx.createLinearGradient(width / 2, 0, 0, height);
      grd.addColorStop(0, "#dd6200");
      grd.addColorStop(1, "#fff400");
      circleObj.color = grd;
      drawCircle(circleObj);
    }
    // 绘制最上面的指针
    function circlingPointer()
    {
      let radius = height / 2 - 60;
      ctx.save(); //保存之前的状态 
      ctx.translate(width / 2, height / 2);//原点移动到画布中央
      ctx.rotate(defaultAngle);//根据角度改变来旋转白色圆点
      // 通过lineTo()和arc方法绘制内环的指针
      ctx.beginPath();
      ctx.moveTo(radius, -10);
      ctx.lineTo(radius + 30, 0);
      ctx.arc(radius, 0, 10, Math.PI * 0.5, Math.PI * 1.5, false);
      ctx.lineTo(radius, 10);
      ctx.fillStyle = '#6de57a';
      ctx.fill();
      ctx.closePath();
      // 绘制内环指针上的圆点 
      ctx.beginPath();
      ctx.arc(radius, 0, 6, 0, Math.PI * 2, false);
      ctx.fillStyle = '#fff';
      ctx.fill();
      ctx.closePath();

      // 绘制外环白色圆点
      ctx.beginPath();
      ctx.arc(width / 2 - 20, 0, 8, 0, 2 * Math.PI, false);
      ctx.fillStyle = "#fff";
      ctx.fill();
      ctx.closePath();
      ctx.restore();//回到未改变坐标的状态
    }
    function drawCircle(circleObj)
    {
      let ctx = circleObj.ctx;
      ctx.beginPath();
      ctx.arc(circleObj.x, circleObj.y, circleObj.radius, circleObj.startAngle, circleObj.endAngle);
      //设定曲线粗细度
      ctx.lineWidth = circleObj.lineWidth;
      //给曲线着色
      ctx.strokeStyle = circleObj.color;
      //连接处样式
      ctx.lineCap = 'round';
      //给环着色
      ctx.stroke();
      ctx.closePath();
    }

    // 另一种使用三角函数进行旋转 
    function circlingMotion()
    {
      let radius = width / 2 - 20;
      ctx.save();
      ctx.translate(0, 0);
      x = width / 2 + Math.cos(defaultAngle) * radius;//确定坐标（此处圆心即是原点）
      y = height / 2 + Math.sin(defaultAngle) * radius;//确定坐标（此处圆心即是原点）
      ctx.beginPath();
      ctx.arc(x, y, 8, 0, 2 * Math.PI, true);//绘画做圆周运动的圆
      ctx.fillStyle = "#fff";
      ctx.fill();
      ctx.closePath();
      ctx.restore();
    }
  </script>
</body>

</html>